#include "test_boost.h"
#include <boost/thread.hpp>
#include <iostream>

using namespace boost::asio;

/**
 * boost::bind实际调用函数
 */
void f(int a, double b)
{
    std::cout << "a = " << a << " b = " << b << std::endl;
}

/**
 * boost::bind函数的使用说明
 */
void testBind()
{
    // boost::bind返回一个函数，函数的参数个数由 _1 占位符表示
    // boost::bind实际调用了另一个函数 f ,占位符的位置表示 f 的第几个参数，此处表示第一个参数
    // boost_f是boost::bind产生的新函数
    auto boost_f = boost::bind(f, _1, 3.14);
    // f 输出结果：a = 2 b = 3.14
    boost_f(2);
}

/**
 * 创建同步客户端
 */
void client()
{
    using namespace std;

    // boost.asio使用io_service和操作系统的输入/输出服务进行交互
    io_service service;
    // 创建地址,端口
    ip::tcp::endpoint ep(ip::address::from_string("127.0.0.1"), 2001);
    // 建立socket
    ip::tcp::socket sock(service);
    try
    {
        // 连接
        sock.connect(ep);
    }
    catch (const boost::system::system_error &e)
    {
        std::cerr << e.code() << '\n';
    }
}

/**
 * 检查是否成功连接,回调函数,类似netty中的 listener
 */
void connect_handler(const boost::system::error_code &ec)
{
}

/**
 * 创建异步客户端
 */
void clientAsync()
{
    io_service service;

    ip::tcp::endpoint ep(ip::address::from_string("127.0.0.1"), 2001);
    ip::tcp::socket sock(service);

    // 每个异步操作都有一个完成处理程序(回调函数)
    sock.async_connect(ep, connect_handler);
    // 只要有待处理的异步操作,service.run()循环就会一直运行
    // 上述只有1个connect异步操作,连接成功之后service.run()就退出了
    service.run();
}

/**
 * 创建同步服务端
 */
void serverSync()
{
    using namespace std;
    io_service service;
    ip::tcp::endpoint ep(ip::tcp::v4(), 2001);
    // 创建一个接收器,用来接受客户端连接的对象
    ip::tcp::acceptor acc(service, ep);

    while (true)
    {
        // 创建一个socket等待客户端的连接
        socket_ptr sock(new ip::tcp::socket(service));
        acc.accept(*sock);

        // 建立一个新线程来处理这个连接
        boost::thread(boost::bind(client_session, sock));
    }
}

/**
 * serverAsync--->io_service
 */
io_service service;
ip::tcp::endpoint ep(ip::tcp::v4(), 2001);
ip::tcp::acceptor acc(service, ep);

/**
 * 所有的异步函数都有抛出错误和返回错误码两种方式的重载
 */
void handle_accept(socket_ptr sock, const boost::system::error_code &err)
{
    if (err)
    {
        return;
    }
    // 处理数据
    // todo

    // 接受新的连接
    socket_ptr sock_(new ip::tcp::socket(service));
    acc.async_accept(*sock, boost::bind(handle_accept, sock_, _1));
}

void signal_handler(const boost::system::error_code &err, int signal)
{
    // 记录日至，然后退出应用
    std::cout << "信号回调运行" << std::endl;
}

/*

  template <typename Protocol1, typename Executor1, typename AcceptHandler>
  BOOST_ASIO_INITFN_RESULT_TYPE(AcceptHandler,void (boost::system::error_code)) async_accept(
            basic_socket<Protocol1, Executor1>& peer,
            BOOST_ASIO_MOVE_ARG(AcceptHandler) handler,
            typename enable_if<is_convertible<Protocol, Protocol1>::value>::type* = 0
        )
# define BOOST_ASIO_INITFN_RESULT_TYPE(ct, sig)
    typename ::boost::asio::async_result<typename ::boost::asio::decay<ct>::type, sig>::return_type

*/

/**
 * 创建异步客户端
 */
void serverAsync()
{
    // socket是线程不安全的，要避免在一个线程中读时，在另一个线程中写入
    socket_ptr sock(new ip::tcp::socket(service));

    // 创建一个 signal_set 实例，指定异步等待的信号量，当这些信号量产生时，就会调用你的异步处理程序
    signal_set sig(service, SIGINT, SIGTERM);
    // 和上边的声明等价
    // signal_set s(service);
    // s.add(SIGINT);
    // s.add(SIGTERM);
    // 当上述添加的信号产生时调用回调函数
    sig.async_wait(signal_handler);

    // 接受完连接之后创建另一个等待客户端连接的异步操作,从而使service.run()循环一直保持忙碌状态
    acc.async_accept(*sock, boost::bind(handle_accept, sock, _1));
    // 这个函数必须等待所有异步操作完成之后才能继续执行，可以在多个线程中调用，会阻塞调用此函数的线程，在一个线程中调用此函数，所有的回调都被调用了
    // 只有调用service.run()；的线程才会调用回调函数
    service.run();
}

/**
 * 线程处理函数,相当于回调函数
 */
void client_session(socket_ptr sock)
{
    using namespace std;
    while (true)
    {
        char data[512] = {0};
        try
        {
            size_t len = sock->read_some(buffer(data));
            if (len > 0)
            {
                write(*sock, buffer("ok", 2));
            }
        }
        catch (const boost::system::system_error &e)
        {
            if (boost::asio::error::eof == e.code())
            {
                // 连接关闭
                cout << "disconnected" << endl;
            }
        }
    }
}

void testSerial()
{
    io_service service;
    serial_port sp(service, "COM7");

    // 设置波特率
    serial_port::baud_rate rate(9600);
    sp.set_option(rate);

    // 设置停止位，奇偶校验位
    // todo config

    // 打开端口之后可以把这个串口看成一个流对串口进行读写操作
    // todo read/write
}
